added Feb 2001 SDK
[windows-sources.git] / shared source / sscli20 / jscript / engine / equality.cs
blobf84790c50c0afe192e430650f30575261523ceb8
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
13 //
14 // ==--==
16 namespace Microsoft.JScript {
18 using Microsoft.JScript.Vsa;
19 using System;
20 using System.Reflection;
21 using System.Reflection.Emit;
22 using System.Diagnostics;
24 public class Equality : BinaryOp{
25 private Object metaData = null;
27 internal Equality(Context context, AST operand1, AST operand2, JSToken operatorTok)
28 : base(context, operand1, operand2, operatorTok) {
31 public Equality(int operatorTok)
32 : base(null, null, null, (JSToken)operatorTok){
35 internal override Object Evaluate(){
36 bool result = this.EvaluateEquality(this.operand1.Evaluate(), this.operand2.Evaluate(), VsaEngine.executeForJSEE);
37 if (this.operatorTok == JSToken.Equal)
38 return result;
39 else
40 return !result;
43 #if !DEBUG
44 [DebuggerStepThroughAttribute]
45 [DebuggerHiddenAttribute]
46 #endif
47 public bool EvaluateEquality(Object v1, Object v2){
48 return EvaluateEquality(v1, v2, false);
52 #if !DEBUG
53 [DebuggerStepThroughAttribute]
54 [DebuggerHiddenAttribute]
55 #endif
56 private bool EvaluateEquality(Object v1, Object v2, bool checkForDebuggerObjects){
57 if (v1 is String && v2 is String) return v1.Equals(v2);
58 if (v1 is Int32 && v2 is Int32) return ((int)v1) == (int)v2;
59 if (v1 is Double && v2 is Double) return ((double)v1) == (double)v2;
60 if ((v2 == null || v2 is DBNull || v2 is Missing) && !checkForDebuggerObjects) return (v1 == null || v1 is DBNull || v1 is Missing);
61 IConvertible ic1 = Convert.GetIConvertible(v1);
62 IConvertible ic2 = Convert.GetIConvertible(v2);
63 TypeCode t1 = Convert.GetTypeCode(v1, ic1);
64 TypeCode t2 = Convert.GetTypeCode(v2, ic2);
65 switch(t1){
66 case TypeCode.Empty:
67 case TypeCode.DBNull:
68 break;
69 case TypeCode.Object:
70 switch(t2){
71 case TypeCode.Empty:
72 case TypeCode.DBNull:
73 break;
74 default:
75 MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
76 if (oper != null){
77 bool result = (bool)oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v1, v2}, null);
78 if (this.operatorTok == JSToken.NotEqual) return !result;
79 return result;
81 break;
83 break;
84 default:
85 switch(t2){
86 case TypeCode.Object:
87 MethodInfo oper = this.GetOperator(v1.GetType(), v2.GetType());
88 if (oper != null){
89 bool result = (bool)oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v1, v2}, null);
90 if (this.operatorTok == JSToken.NotEqual) return !result;
91 return result;
93 break;
95 break;
97 return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, t2, checkForDebuggerObjects);
102 public static bool JScriptEquals(Object v1, Object v2){
103 if (v1 is String && v2 is String) return v1.Equals(v2);
104 if (v1 is Int32 && v2 is Int32) return ((int)v1) == (int)v2;
105 if (v1 is Double && v2 is Double) return ((double)v1) == (double)v2;
106 if ((v2 == null || v2 is DBNull || v2 is Missing)) return (v1 == null || v1 is DBNull || v1 is Missing);
107 IConvertible ic1 = Convert.GetIConvertible(v1);
108 IConvertible ic2 = Convert.GetIConvertible(v2);
109 TypeCode t1 = Convert.GetTypeCode(v1, ic1);
110 TypeCode t2 = Convert.GetTypeCode(v2, ic2);
111 return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, t2, false);
114 private static bool JScriptEquals(Object v1, Object v2, IConvertible ic1, IConvertible ic2, TypeCode t1, TypeCode t2, bool checkForDebuggerObjects){
115 if (StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, t1, t2, checkForDebuggerObjects))
116 return true;
117 if (t2 == TypeCode.Boolean){
118 v2 = ic2.ToBoolean(null) ? 1 : 0;
119 ic2 = Convert.GetIConvertible(v2);
120 return Equality.JScriptEquals(v1, v2, ic1, ic2, t1, TypeCode.Int32, false);
122 switch (t1){
123 case TypeCode.Empty: return t2 == TypeCode.Empty || t2 == TypeCode.DBNull || (t2 == TypeCode.Object && v2 is Missing);
124 case TypeCode.Object:
125 switch (t2){
126 case TypeCode.Empty:
127 case TypeCode.DBNull:
128 return v1 is Missing;
129 case TypeCode.Char:
130 case TypeCode.SByte:
131 case TypeCode.Byte:
132 case TypeCode.Int16:
133 case TypeCode.UInt16:
134 case TypeCode.Int32:
135 case TypeCode.UInt32:
136 case TypeCode.Int64:
137 case TypeCode.UInt64:
138 case TypeCode.Single:
139 case TypeCode.Double:
140 case TypeCode.Decimal:
141 case TypeCode.String:
142 IConvertible pvic1 = ic1;
143 Object pv1 = Convert.ToPrimitive(v1, PreferredType.Either, ref pvic1);
144 if (pvic1 != null && pv1 != v1)
145 return Equality.JScriptEquals(pv1, v2, pvic1, ic2, pvic1.GetTypeCode(), t2, false);
146 else
147 return false;
149 return false;
150 case TypeCode.DBNull: return t2 == TypeCode.DBNull || t2 == TypeCode.Empty || (t2 == TypeCode.Object && v2 is Missing);
151 case TypeCode.Boolean:
152 v1 = ic1.ToBoolean(null) ? 1 : 0;
153 ic1 = Convert.GetIConvertible(v1);
154 return Equality.JScriptEquals(v1, v2, ic1, ic2, TypeCode.Int32, t2, false);
155 case TypeCode.Char:
156 case TypeCode.SByte:
157 case TypeCode.Byte:
158 case TypeCode.Int16:
159 case TypeCode.UInt16:
160 case TypeCode.Int32:
161 case TypeCode.UInt32:
162 case TypeCode.Int64:
163 case TypeCode.UInt64:
164 case TypeCode.Single:
165 case TypeCode.Double:
166 case TypeCode.Decimal:
167 if (t2 == TypeCode.Object){
168 IConvertible pvic2 = ic2;
169 Object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
170 if (pvic2 != null && pv2 != v2)
171 return Equality.JScriptEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
172 else
173 return false;
175 if (t2 == TypeCode.String){
176 if (v1 is Enum) return Convert.ToString(v1).Equals(ic2.ToString(null));
177 v2 = Convert.ToNumber(v2, ic2);
178 ic2 = Convert.GetIConvertible(v2);
179 return StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, t1, TypeCode.Double, false);
181 return false;
182 case TypeCode.DateTime:
183 if (t2 == TypeCode.Object){
184 IConvertible pvic2 = ic2;
185 Object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
186 if (pv2 != null && pv2 != v2)
187 return StrictEquality.JScriptStrictEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
189 return false;
190 case TypeCode.String:
191 switch (t2){
192 case TypeCode.Object:{
193 IConvertible pvic2 = ic2;
194 Object pv2 = Convert.ToPrimitive(v2, PreferredType.Either, ref pvic2);
195 if (pvic2 != null && pv2 != v2)
196 return Equality.JScriptEquals(v1, pv2, ic1, pvic2, t1, pvic2.GetTypeCode(), false);
197 else
198 return false;
200 case TypeCode.SByte:
201 case TypeCode.Byte:
202 case TypeCode.Int16:
203 case TypeCode.UInt16:
204 case TypeCode.Int32:
205 case TypeCode.UInt32:
206 case TypeCode.Int64:
207 case TypeCode.UInt64:
208 case TypeCode.Single:
209 case TypeCode.Double:
210 case TypeCode.Decimal:
211 if (v2 is Enum) return Convert.ToString(v2).Equals(ic1.ToString(null));
212 v1 = Convert.ToNumber(v1, ic1);
213 ic1 = Convert.GetIConvertible(v1);
214 return StrictEquality.JScriptStrictEquals(v1, v2, ic1, ic2, TypeCode.Double, t2, false);
216 return false;
218 return false;
221 internal override IReflect InferType(JSField inference_target){
222 return Typeob.Boolean;
225 internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm){
226 if (this.metaData == null){
227 Type t1 = this.type1;
228 Type t2 = this.type2;
229 Type t3 = Typeob.Object;
230 bool emitNullAndUndefined = true;
231 if (t1.IsPrimitive && t2.IsPrimitive){
232 t3 = Typeob.Double;
233 if (t1 == Typeob.Single || t2 == Typeob.Single)
234 t3 = Typeob.Single;
235 else if (Convert.IsPromotableTo(t1, t2))
236 t3 = t2;
237 else if (Convert.IsPromotableTo(t2, t1))
238 t3 = t1;
239 }else if (t1 == Typeob.String && (t2 == Typeob.String || t2 == Typeob.Empty || t2 == Typeob.Null)){
240 t3 = Typeob.String;
241 if (t2 != Typeob.String){
242 emitNullAndUndefined = false;
243 branchIfTrue = !branchIfTrue;
245 }else if ((t1 == Typeob.Empty || t1 == Typeob.Null) && t2 == Typeob.String){
246 t3 = Typeob.String;
247 emitNullAndUndefined = false;
248 branchIfTrue = !branchIfTrue;
250 if (t3 == Typeob.SByte || t3 == Typeob.Int16)
251 t3 = Typeob.Int32;
252 else if (t3 == Typeob.Byte || t3 == Typeob.UInt16)
253 t3 = Typeob.UInt32;
254 if (emitNullAndUndefined){
255 this.operand1.TranslateToIL(il, t3);
256 this.operand2.TranslateToIL(il, t3);
257 if (t3 == Typeob.Object)
258 il.Emit(OpCodes.Call, CompilerGlobals.jScriptEqualsMethod);
259 else if (t3 == Typeob.String)
260 il.Emit(OpCodes.Call, CompilerGlobals.stringEqualsMethod);
261 }else if (t1 == Typeob.String)
262 this.operand1.TranslateToIL(il, t3);
263 else if (t2 == Typeob.String)
264 this.operand2.TranslateToIL(il, t3);
265 if (branchIfTrue){
266 if (this.operatorTok == JSToken.Equal)
267 if (t3 == Typeob.String || t3 == Typeob.Object)
268 il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
269 else
270 il.Emit(shortForm ? OpCodes.Beq_S : OpCodes.Beq, label);
271 else
272 if (t3 == Typeob.String || t3 == Typeob.Object)
273 il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
274 else
275 il.Emit(shortForm ? OpCodes.Bne_Un_S : OpCodes.Bne_Un, label);
276 }else{
277 if (this.operatorTok == JSToken.Equal)
278 if (t3 == Typeob.String || t3 == Typeob.Object)
279 il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
280 else
281 il.Emit(shortForm ? OpCodes.Bne_Un_S : OpCodes.Bne_Un, label);
282 else
283 if (t3 == Typeob.String || t3 == Typeob.Object)
284 il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
285 else
286 il.Emit(shortForm ? OpCodes.Beq_S : OpCodes.Beq, label);
288 return;
290 if (this.metaData is MethodInfo){
291 MethodInfo oper = (MethodInfo)this.metaData;
292 ParameterInfo[] pars = oper.GetParameters();
293 this.operand1.TranslateToIL(il, pars[0].ParameterType);
294 this.operand2.TranslateToIL(il, pars[1].ParameterType);
295 il.Emit(OpCodes.Call, oper);
296 if (branchIfTrue)
297 il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
298 else
299 il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
300 return;
302 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
303 //Compile operands to objects and devolve the decision making to run time thunks
304 il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
305 this.operand1.TranslateToIL(il, Typeob.Object);
306 this.operand2.TranslateToIL(il, Typeob.Object);
307 il.Emit(OpCodes.Call, CompilerGlobals.evaluateEqualityMethod);
308 if (branchIfTrue){
309 if (this.operatorTok == JSToken.Equal)
310 il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
311 else
312 il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
313 }else{
314 if (this.operatorTok == JSToken.Equal)
315 il.Emit(shortForm ? OpCodes.Brfalse_S : OpCodes.Brfalse, label);
316 else
317 il.Emit(shortForm ? OpCodes.Brtrue_S : OpCodes.Brtrue, label);
319 return;
322 internal override void TranslateToIL(ILGenerator il, Type rtype){
323 Label true_label = il.DefineLabel();
324 Label done_label = il.DefineLabel();
325 this.TranslateToConditionalBranch(il, true, true_label, true);
326 il.Emit(OpCodes.Ldc_I4_0);
327 il.Emit(OpCodes.Br_S, done_label);
328 il.MarkLabel(true_label);
329 il.Emit(OpCodes.Ldc_I4_1);
330 il.MarkLabel(done_label);
331 Convert.Emit(this, il, Typeob.Boolean, rtype);
334 internal override void TranslateToILInitializer(ILGenerator il){
335 this.operand1.TranslateToILInitializer(il);
336 this.operand2.TranslateToILInitializer(il);
337 MethodInfo oper = this.GetOperator(this.operand1.InferType(null), this.operand2.InferType(null));
338 if (oper != null){
339 this.metaData = oper;
340 return;
342 if (this.operand1 is ConstantWrapper){
343 Object val = this.operand1.Evaluate();
344 if (val == null) this.type1 = Typeob.Empty;
345 else if (val is DBNull) this.type1 = Typeob.Null;
347 if (this.operand2 is ConstantWrapper){
348 Object val = this.operand2.Evaluate();
349 if (val == null) this.type2 = Typeob.Empty;
350 else if (val is DBNull) this.type2 = Typeob.Null;
352 if (this.type1 == Typeob.Empty || this.type1 == Typeob.Null || this.type2 == Typeob.Empty || this.type2 == Typeob.Null) return;
353 if ((this.type1.IsPrimitive || this.type1 == Typeob.String || Typeob.JSObject.IsAssignableFrom(this.type1))
354 && (this.type2.IsPrimitive || this.type2 == Typeob.String || Typeob.JSObject.IsAssignableFrom(this.type2)))
355 return;
356 this.metaData = il.DeclareLocal(Typeob.Equality);
357 ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
358 il.Emit(OpCodes.Newobj, CompilerGlobals.equalityConstructor);
359 il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);